Merge "ActivityThread: Preserve windows when relaunching all activities" into qt-dev
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index aa35a6f..0260faa 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -28,6 +28,8 @@
 import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
 import static android.view.Display.INVALID_DISPLAY;
 
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
@@ -4608,7 +4610,7 @@
     private void onCoreSettingsChange() {
         if (updateDebugViewAttributeState()) {
             // request all activities to relaunch for the changes to take place
-            relaunchAllActivities();
+            relaunchAllActivities(false /* preserveWindows */);
         }
     }
 
@@ -4625,10 +4627,13 @@
         return previousState != View.sDebugViewAttributes;
     }
 
-    private void relaunchAllActivities() {
+    private void relaunchAllActivities(boolean preserveWindows) {
         for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
-            final Activity activity = entry.getValue().activity;
-            if (!activity.mFinished) {
+            final ActivityClientRecord r = entry.getValue();
+            if (!r.activity.mFinished) {
+                if (preserveWindows && r.window != null) {
+                    r.mPreserveWindow = true;
+                }
                 scheduleRelaunchActivity(entry.getKey());
             }
         }
@@ -5461,7 +5466,8 @@
         }
     }
 
-    void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) {
+    @VisibleForTesting(visibility = PACKAGE)
+    public void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) {
         // Updates triggered by package installation go through a package update
         // receiver. Here we try to capture ApplicationInfo changes that are
         // caused by other sources, such as overlays. That means we want to be as conservative
@@ -5507,7 +5513,8 @@
         newConfig.assetsSeq = (mConfiguration != null ? mConfiguration.assetsSeq : 0) + 1;
         handleConfigurationChanged(newConfig, null);
 
-        relaunchAllActivities();
+        // Preserve windows to avoid black flickers when overlays change.
+        relaunchAllActivities(true /* preserveWindows */);
     }
 
     static void freeTextLayoutCachesIfNeeded(int configDiff) {
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 9cb3489..711eaa7 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -38,6 +38,7 @@
 import android.os.IBinder;
 import android.util.MergedConfiguration;
 import android.view.Display;
+import android.view.View;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
@@ -153,6 +154,34 @@
     }
 
     @Test
+    public void testHandleActivity_assetsChanged() {
+        final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+
+        final IBinder[] token = new IBinder[1];
+        final View[] decorView = new View[1];
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            final ActivityThread activityThread = activity.getActivityThread();
+
+            token[0] = activity.getActivityToken();
+            decorView[0] = activity.getWindow().getDecorView();
+
+            // Relaunches all activities
+            activityThread.handleApplicationInfoChanged(activity.getApplicationInfo());
+        });
+
+        final View[] newDecorView = new View[1];
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            final ActivityThread activityThread = activity.getActivityThread();
+
+            final Activity newActivity = activityThread.getActivity(token[0]);
+            newDecorView[0] = activity.getWindow().getDecorView();
+        });
+
+        assertEquals("Window must be preserved", decorView[0], newDecorView[0]);
+    }
+
+    @Test
     public void testHandleActivityConfigurationChanged_DropStaleConfigurations() {
         final TestActivity activity = mActivityTestRule.launchActivity(new Intent());