From 51bbfe1c60b0c3e2cdba9acbc6a86ab516c8b669 Mon Sep 17 00:00:00 2001 From: Harry Yu Date: Tue, 23 May 2023 08:15:00 -0700 Subject: [PATCH] Prevent crash in runAnimationStep on OnePlus and Oppo devices (#37487) Summary: We've been encountering a crash in `runAnimationStep` with "Calculated frame index should never be lower than 0" https://github.com/facebook/react-native/issues/35766 with OnePlus/Oppo devices as well, but don't have one on hand to test. This just works around the issue: if the time is before the start time of an animation, we shouldn't do anything anyways, so we just log a message instead of throwing while in production. We still throw in debug mode though for easier debugging. ### Hypothesis of the root cause Based on stacktrace in https://github.com/facebook/react-native/issues/35766 (which is the same one we see) Normally, this should happen 1. Choreographer.java constructs a FrameDisplayEventReceiver 2. FrameDisplayEventReceiver.onVSync gets called, which sets the `mTimestampNanos` 3. FrameDisplayEventReceiver.run gets called, which then eventually calls our `doFrame` callback with `mTimestampNanos`. This then causes `FrameBasedAnimationDriver.runAnimationStep` to be called with the same timestamp I suspect what's happening on OnePlus devices is that the `onVSync` call either doesn't happen or happens rarely enough that the `mTimestampNanos` when `run` is called is sometime in the past ### Fix 1. Add logging so we get the parameters to debug more if we end up getting this error 2. In production, just ignore past times instead of throwing an Error ## Changelog: Pick one each for the category and type tags: [ANDROID] [FIXED] - Prevent crash on OnePlus/Oppo devices in runAnimationStep Pull Request resolved: https://github.com/facebook/react-native/pull/37487 Test Plan: Ran our app using patched version and verified no issues showed up when using it Reviewed By: cipolleschi Differential Revision: D46102968 Pulled By: cortinico fbshipit-source-id: bcb36a0c2aed0afdb8e7e68b141a3db4eb02695a --- .../react/animated/FrameBasedAnimationDriver.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/FrameBasedAnimationDriver.java b/ReactAndroid/src/main/java/com/facebook/react/animated/FrameBasedAnimationDriver.java index 798fcffd7442ad..711e050e680053 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/animated/FrameBasedAnimationDriver.java +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/FrameBasedAnimationDriver.java @@ -7,9 +7,12 @@ package com.facebook.react.animated; +import com.facebook.common.logging.FLog; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableType; +import com.facebook.react.common.ReactConstants; +import com.facebook.react.common.build.ReactBuildConfig; /** * Implementation of {@link AnimationDriver} which provides a support for simple time-based @@ -70,7 +73,17 @@ public void runAnimationStep(long frameTimeNanos) { long timeFromStartMillis = (frameTimeNanos - mStartFrameTimeNanos) / 1000000; int frameIndex = (int) Math.round(timeFromStartMillis / FRAME_TIME_MILLIS); if (frameIndex < 0) { - throw new IllegalStateException("Calculated frame index should never be lower than 0"); + String message = + "Calculated frame index should never be lower than 0. Called with frameTimeNanos " + + frameTimeNanos + + " and mStartFrameTimeNanos " + + mStartFrameTimeNanos; + if (ReactBuildConfig.DEBUG) { + throw new IllegalStateException(message); + } else { + FLog.w(ReactConstants.TAG, message); + return; + } } else if (mHasFinished) { // nothing to do here return;