Replace AnimationHandler's ThreadLocal with

... a bridge context specific thread local to ensure each
render session has its own instance of AnimationHandler.

This is to prevent issues due to one render session
disposing the callbacks that were registered in another.

Bug: 282724670
Test: manual
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:b41cb105a0fc7dc23ee740cf23b2928800dba075)
Merged-In: I2e48c3eea43752259f1eba3ae9bf662cc6558b0a
Change-Id: I2e48c3eea43752259f1eba3ae9bf662cc6558b0a
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 9f8a8f5..67fb534 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -40,6 +40,7 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import android.animation.AnimationHandler;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -206,6 +207,7 @@
     private final Map<Key<?>, Object> mUserData = new HashMap<>();
 
     private final SessionInteractiveData mSessionInteractiveData;
+    private final ThreadLocal<AnimationHandler> mAnimationHandlerThreadLocal = new ThreadLocal<>();
 
     /**
      * Some applications that target both pre API 17 and post API 17, set the newer attrs to
@@ -2298,4 +2300,9 @@
     public void applyWallpaper(String wallpaperPath) {
         mRenderResources.setWallpaper(wallpaperPath, mConfig.isNightModeActive());
     }
+
+    @NotNull
+    public ThreadLocal<AnimationHandler> getAnimationHandlerThreadLocal() {
+        return mAnimationHandlerThreadLocal;
+    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index 4288c9f..8048ff1 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -32,6 +32,7 @@
 import com.android.tools.layoutlib.annotations.Nullable;
 import com.android.tools.layoutlib.annotations.VisibleForTesting;
 
+import android.animation.AnimationHandler;
 import android.animation.PropertyValuesHolder_Accessor;
 import android.content.res.Configuration;
 import android.graphics.drawable.AdaptiveIconDrawable_Delegate;
@@ -284,6 +285,7 @@
         ILayoutLog currentLog = mParams.getLog();
         Bridge.setLog(currentLog);
         mContext.getRenderResources().setLogger(currentLog);
+        AnimationHandler.sAnimatorHandler = mContext.getAnimationHandlerThreadLocal();
     }
 
     /**
@@ -475,6 +477,14 @@
         if (sCurrentContext != null) {
             // quit HandlerThread created during this session.
             HandlerThread_Delegate.cleanUp(sCurrentContext);
+
+            AnimationHandler animationHandler =
+                    sCurrentContext.getAnimationHandlerThreadLocal().get();
+            if (animationHandler != null) {
+                animationHandler.mDelayedCallbackStartTime.clear();
+                animationHandler.mAnimationCallbacks.clear();
+                animationHandler.mCommitCallbacks.clear();
+            }
         }
 
         sCurrentContext = null;
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 375891a..faeeb19 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -50,7 +50,6 @@
 import com.android.tools.idea.validator.hierarchy.CustomHierarchyHelper;
 import com.android.tools.layoutlib.annotations.NotNull;
 
-import android.animation.AnimationHandler;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -1210,12 +1209,6 @@
             mImage = null;
             // detachFromWindow might create Handler callbacks, thus before Handler_Delegate.dispose
             AttachInfo_Accessor.detachFromWindow(mViewRoot);
-            AnimationHandler animationHandler = AnimationHandler.sAnimatorHandler.get();
-            if (animationHandler != null) {
-                animationHandler.mDelayedCallbackStartTime.clear();
-                animationHandler.mAnimationCallbacks.clear();
-                animationHandler.mCommitCallbacks.clear();
-            }
             getContext().getSessionInteractiveData().dispose();
             if (mViewInfoList != null) {
                 mViewInfoList.clear();
diff --git a/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 4489966..b2b7e70 100644
--- a/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -390,7 +390,7 @@
      * List of fields for which we will remove the final modifier.
      */
     private final static String[] REMOVED_FINAL_MODIFIER_FIELDS =
-            new String[]{};
+            new String[]{"android.animation.AnimationHandler#sAnimatorHandler"};
 
     public static class LinkedHashMapEldestReplacer implements MethodReplacer {