Merge "Allow Activities to handle configuration changes" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 889b627..9ad8dd3 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12047,6 +12047,7 @@
     field public static final int COLOR_MODE_DEFAULT = 0; // 0x0
     field public static final int COLOR_MODE_HDR = 2; // 0x2
     field public static final int COLOR_MODE_WIDE_COLOR_GAMUT = 1; // 0x1
+    field @FlaggedApi("android.content.res.handle_all_config_changes") public static final int CONFIG_ASSETS_PATHS = -2147483648; // 0x80000000
     field public static final int CONFIG_COLOR_MODE = 16384; // 0x4000
     field public static final int CONFIG_DENSITY = 4096; // 0x1000
     field public static final int CONFIG_FONT_SCALE = 1073741824; // 0x40000000
@@ -12060,6 +12061,7 @@
     field public static final int CONFIG_MNC = 2; // 0x2
     field public static final int CONFIG_NAVIGATION = 64; // 0x40
     field public static final int CONFIG_ORIENTATION = 128; // 0x80
+    field @FlaggedApi("android.content.res.handle_all_config_changes") public static final int CONFIG_RESOURCES_UNUSED = 134217728; // 0x8000000
     field public static final int CONFIG_SCREEN_LAYOUT = 256; // 0x100
     field public static final int CONFIG_SCREEN_SIZE = 1024; // 0x400
     field public static final int CONFIG_SMALLEST_SCREEN_SIZE = 2048; // 0x800
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b384326..7bea0489 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -30,6 +30,7 @@
 import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
 import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS;
 import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
+import static android.content.pm.ActivityInfo.CONFIG_RESOURCES_UNUSED;
 import static android.content.res.Configuration.UI_MODE_TYPE_DESK;
 import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -6520,6 +6521,13 @@
             return true;
         }
 
+        if (android.content.res.Flags.handleAllConfigChanges()) {
+            if ((handledConfigChanges & CONFIG_RESOURCES_UNUSED) != 0) {
+                // Report the change if activities claim they do not use resources at all.
+                return true;
+            }
+        }
+
         final int diffWithBucket = SizeConfigurationBuckets.filterDiff(publicDiff, currentConfig,
                 newConfig, sizeBuckets);
         // Compare to the diff which filter the change without crossing size buckets with
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 57ffed4..6952a09 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -941,6 +941,8 @@
             CONFIG_COLOR_MODE,
             CONFIG_FONT_SCALE,
             CONFIG_GRAMMATICAL_GENDER,
+            CONFIG_ASSETS_PATHS,
+            CONFIG_RESOURCES_UNUSED,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Config {}
@@ -1060,8 +1062,8 @@
      * can itself handle asset path changes.  Set from the {@link android.R.attr#configChanges}
      * attribute. This is not a core resource configuration, but a higher-level value, so its
      * constant starts at the high bits.
-     * @hide We do not want apps handling this yet, but we do need some kind of bit for diffs.
      */
+    @FlaggedApi(android.content.res.Flags.FLAG_HANDLE_ALL_CONFIG_CHANGES)
     public static final int CONFIG_ASSETS_PATHS = 0x80000000;
     /**
      * Bit in {@link #configChanges} that indicates that the activity
@@ -1088,6 +1090,30 @@
      */
     public static final int CONFIG_FONT_WEIGHT_ADJUSTMENT = 0x10000000;
 
+    /**
+     * <p>This is probably not the constant you want, the resources compiler supports a less
+     * dangerous version of it, 'allKnown', that only suppresses all currently existing
+     * configuration change restarts depending on your target SDK rather than whatever the latest
+     * SDK supports, allowing the application to work with resources on future Platform versions.
+     *
+     * <p>Bit in {@link #configChanges} that indicates that the activity doesn't use Android
+     * Resources at all and doesn't need to be restarted on any configuration changes. This bit
+     * disables all restarts for configuration dimensions available in the current target SDK as
+     * well as dimensions introduced in future SDKs. Use it only if the activity doesn't need
+     * anything from its resources, and doesn't depend on any libraries that may provide resources
+     * and need to respond to configuration changes. When set,
+     * {@link Activity#onConfigurationChanged(Configuration)} will be called instead of a restart,
+     * and it’s up to the implementation to ensure that no stale resource values remain loaded
+     * anywhere in the code.
+     *
+     * <p>This overrides all other bits, and this is recommended to be used individually.
+     *
+     * <p>This is not a core resource configuration, but a higher-level value, so its constant
+     * starts at the high bits.
+     */
+    @FlaggedApi(android.content.res.Flags.FLAG_HANDLE_ALL_CONFIG_CHANGES)
+    public static final int CONFIG_RESOURCES_UNUSED = 0x8000000;
+
     /** @hide
      * Unfortunately the constants for config changes in native code are
      * different from ActivityInfo. :(  Here are the values we should use for the
@@ -1657,7 +1683,8 @@
      * {@link #CONFIG_KEYBOARD}, {@link #CONFIG_NAVIGATION},
      * {@link #CONFIG_ORIENTATION}, {@link #CONFIG_SCREEN_LAYOUT},
      * {@link #CONFIG_DENSITY}, {@link #CONFIG_LAYOUT_DIRECTION},
-     * {@link #CONFIG_COLOR_MODE}, and {link #CONFIG_GRAMMATICAL_GENDER}.
+     * {@link #CONFIG_COLOR_MODE}, {@link #CONFIG_GRAMMATICAL_GENDER},
+     * {@link #CONFIG_ASSETS_PATHS}, and {@link #CONFIG_RESOURCES_UNUSED}.
      * Set from the {@link android.R.attr#configChanges} attribute.
      */
     public int configChanges;
diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
index a475cc8..a5f8199 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -56,3 +56,13 @@
     # This flag is read in ResourcesImpl at boot time.
     is_fixed_read_only: true
 }
+
+flag {
+    name: "handle_all_config_changes"
+    is_exported: true
+    namespace: "resource_manager"
+    description: "Feature flag for allowing activities to handle all kinds of configuration changes"
+    bug: "180625460"
+    # This flag is read at boot time.
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/inputmethodservice/ImsConfigurationTracker.java b/core/java/android/inputmethodservice/ImsConfigurationTracker.java
index 30ef0a2..17a5ffc 100644
--- a/core/java/android/inputmethodservice/ImsConfigurationTracker.java
+++ b/core/java/android/inputmethodservice/ImsConfigurationTracker.java
@@ -16,6 +16,8 @@
 
 package android.inputmethodservice;
 
+import static android.content.pm.ActivityInfo.CONFIG_RESOURCES_UNUSED;
+
 import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -88,12 +90,16 @@
         if (!mInitialized) {
             return;
         }
+        // Don't restart InputMethodService that claims it does not use resources at all.
+        boolean neverReset = android.content.res.Flags.handleAllConfigChanges()
+                && (mHandledConfigChanges & CONFIG_RESOURCES_UNUSED) != 0;
+
         final int diff = mLastKnownConfig != null
                 ? mLastKnownConfig.diffPublicOnly(newConfig) : CONFIG_CHANGED;
         // If the new config is the same as the config this Service is already running with,
         // then don't bother calling resetStateForNewConfiguration.
         final int unhandledDiff = (diff & ~mHandledConfigChanges);
-        if (unhandledDiff != 0) {
+        if (unhandledDiff != 0 && !neverReset) {
             resetStateForNewConfigurationRunner.run();
         }
         if (diff != 0) {
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index f94c8ab..2e3dbda 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1052,6 +1052,19 @@
         <!-- The font weight adjustment value has changed. Used to reflect the user increasing font
              weight. -->
         <flag name="fontWeightAdjustment" value="0x10000000" />
+        <!-- The assets paths have changed. For example a runtime overlay is installed and enabled.
+             Corresponds to {@link android.content.pm.ActivityInfo#CONFIG_ASSETS_PATHS}. -->
+        <flag name="assetsPaths" value="0x80000000" />
+        <!-- This is probably not the flag you want, the resources compiler supports a less
+             dangerous version of it, 'allKnown', that only suppresses all currently existing
+             configuration change restarts depending on your target SDK rather than whatever the
+             latest SDK supports, allowing the application to work with resources on future Platform
+             versions.
+             Activity doesn't use Android Resources at all and doesn't need to be restarted on any
+             configuration changes. This overrides all other flags, and this is recommended to be
+             used individually. Corresponds to
+             {@link android.content.pm.ActivityInfo#CONFIG_RESOURCES_UNUSED}. -->
+        <flag name="resourcesUnused" value="0x8000000" />
     </attr>
 
     <!-- Indicate that the activity can be launched as the embedded child of another
diff --git a/core/tests/coretests/src/android/inputmethodservice/ImsConfigurationTrackerTest.java b/core/tests/coretests/src/android/inputmethodservice/ImsConfigurationTrackerTest.java
index 064439e..6998c32 100644
--- a/core/tests/coretests/src/android/inputmethodservice/ImsConfigurationTrackerTest.java
+++ b/core/tests/coretests/src/android/inputmethodservice/ImsConfigurationTrackerTest.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
+import android.platform.test.annotations.RequiresFlagsEnabled;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -74,4 +75,33 @@
         assertFalse("IME shouldn't restart since it handles configChanges",
                 didReset.get());
     }
+
+    @Test
+    @RequiresFlagsEnabled(android.content.res.Flags.FLAG_HANDLE_ALL_CONFIG_CHANGES)
+    public void testShouldImeRestart_handleResourceUnused() throws Exception {
+        Configuration config = mContext.getResources().getConfiguration();
+        mImsConfigTracker.onInitialize(0 /* handledConfigChanges */);
+        mImsConfigTracker.onBindInput(mContext.getResources());
+        Configuration newConfig = new Configuration(config);
+
+        final AtomicBoolean didReset = new AtomicBoolean();
+        Runnable resetStateRunner = () -> didReset.set(true);
+
+        mImsConfigTracker.onConfigurationChanged(newConfig, resetStateRunner);
+        assertFalse("IME shouldn't restart if config hasn't changed",
+                didReset.get());
+
+        // Screen density changed but IME doesn't handle configChanges
+        newConfig.densityDpi = 99;
+        mImsConfigTracker.onConfigurationChanged(newConfig, resetStateRunner);
+        assertTrue("IME should restart for unhandled configChanges",
+                didReset.get());
+
+        didReset.set(false);
+        // opt-in IME to handle all configuration changes.
+        mImsConfigTracker.setHandledConfigChanges(ActivityInfo.CONFIG_RESOURCES_UNUSED);
+        mImsConfigTracker.onConfigurationChanged(newConfig, resetStateRunner);
+        assertFalse("IME shouldn't restart since it handles configChanges",
+                didReset.get());
+    }
 }
diff --git a/core/tests/overlaytests/handle_config_change/Android.bp b/core/tests/overlaytests/handle_config_change/Android.bp
new file mode 100644
index 0000000..2b31d0a
--- /dev/null
+++ b/core/tests/overlaytests/handle_config_change/Android.bp
@@ -0,0 +1,45 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+    default_team: "trendy_team_android_resources",
+}
+
+java_test_host {
+    name: "HandleConfigChangeHostTests",
+    srcs: ["src/**/*.java"],
+    libs: [
+        "tradefed",
+        "compatibility-host-util",
+    ],
+    static_libs: [
+        "compatibility-host-util-axt",
+        "flag-junit-host",
+        "android.content.res.flags-aconfig-java-host",
+    ],
+    test_suites: [
+        "device-tests",
+    ],
+    // All APKs required by the tests
+    data: [
+        ":OverlayResApp",
+    ],
+    per_testcase_directory: true,
+}
diff --git a/core/tests/overlaytests/handle_config_change/AndroidTest.xml b/core/tests/overlaytests/handle_config_change/AndroidTest.xml
new file mode 100644
index 0000000..05ea036
--- /dev/null
+++ b/core/tests/overlaytests/handle_config_change/AndroidTest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<configuration description="Config for the handle config change test cases">
+    <option name="test-tag" value="HandleConfigChangeHostTests" />
+    <option name="test-suite-tag" value="apct" />
+
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="force-skip-system-props" value="true" />
+        <option name="set-global-setting" key="verifier_engprod" value="1" />
+        <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
+        <option name="restore-settings" value="true" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="OverlayResApp.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.HostTest">
+        <option name="class" value="com.android.overlaytest.HandleConfigChangeHostTests" />
+    </test>
+</configuration>
diff --git a/core/tests/overlaytests/handle_config_change/src/com/android/overlaytest/HandleConfigChangeHostTests.java b/core/tests/overlaytests/handle_config_change/src/com/android/overlaytest/HandleConfigChangeHostTests.java
new file mode 100644
index 0000000..e91716a
--- /dev/null
+++ b/core/tests/overlaytests/handle_config_change/src/com/android/overlaytest/HandleConfigChangeHostTests.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.overlaytest;
+
+import android.content.res.Flags;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.host.HostFlagsValueProvider;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
+public class HandleConfigChangeHostTests extends BaseHostJUnit4Test {
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            HostFlagsValueProvider.createCheckFlagsRule(this::getDevice);
+    private static final String DEVICE_TEST_PKG1 = "com.android.overlaytest.overlayresapp";
+    private static final String DEVICE_TEST_CLASS = "OverlayResTest";
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_HANDLE_ALL_CONFIG_CHANGES)
+    public void testOverlayRes() throws Exception {
+        runDeviceTests(DEVICE_TEST_PKG1, DEVICE_TEST_PKG1 + "." + DEVICE_TEST_CLASS,
+                "overlayRes_onConfigurationChanged");
+    }
+}
diff --git a/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/Android.bp b/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/Android.bp
new file mode 100644
index 0000000..e0f1012
--- /dev/null
+++ b/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/Android.bp
@@ -0,0 +1,43 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+    name: "OverlayResApp",
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+    platform_apis: true,
+    certificate: "platform",
+
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.test.rules",
+        "androidx.test.core",
+        "compatibility-device-util-axt",
+        "truth",
+    ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    test_suites: [
+        "device-tests",
+    ],
+
+    manifest: "AndroidManifest.xml",
+}
diff --git a/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/AndroidManifest.xml b/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/AndroidManifest.xml
new file mode 100644
index 0000000..617e879
--- /dev/null
+++ b/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.overlaytest.overlayresapp"
+    xmlns:tools="http://schemas.android.com/tools">
+
+  <application>
+    <uses-library android:name="android.test.runner" />
+    <activity
+        android:name=".OverlayResActivity"
+        android:exported="false"
+        android:configChanges="assetsPaths">
+    </activity>
+  </application>
+
+  <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                   android:targetPackage="com.android.overlaytest.overlayresapp" />
+
+</manifest>
diff --git a/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/res/values/integers.xml b/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/res/values/integers.xml
new file mode 100644
index 0000000..493333f
--- /dev/null
+++ b/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/res/values/integers.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <integer name="test_integer">0</integer>
+</resources>
\ No newline at end of file
diff --git a/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/res/values/strings.xml b/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/res/values/strings.xml
new file mode 100644
index 0000000..72820d3
--- /dev/null
+++ b/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+  <string name="app_name">My Application</string>
+  <string name="test_string">Test String</string>
+</resources>
\ No newline at end of file
diff --git a/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/src/com/android/overlaytest/overlayresapp/OverlayResActivity.java b/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/src/com/android/overlaytest/overlayresapp/OverlayResActivity.java
new file mode 100644
index 0000000..96143ae
--- /dev/null
+++ b/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/src/com/android/overlaytest/overlayresapp/OverlayResActivity.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.overlaytest.overlayresapp;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * A test activity to verify that the assets paths configuration changes are received if the
+ * overlay targeting state is changed.
+ */
+public class OverlayResActivity extends Activity {
+    private Runnable mConfigurationChangedCallback;
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    public void onConfigurationChanged(@NonNull Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        final Runnable callback = mConfigurationChangedCallback;
+        if (callback != null) {
+            callback.run();
+        }
+    }
+
+    /** Registers the callback of onConfigurationChanged. */
+    public void setConfigurationChangedCallback(Runnable callback) {
+        mConfigurationChangedCallback = callback;
+    }
+}
diff --git a/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/src/com/android/overlaytest/overlayresapp/OverlayResTest.java b/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/src/com/android/overlaytest/overlayresapp/OverlayResTest.java
new file mode 100644
index 0000000..1c37719
--- /dev/null
+++ b/core/tests/overlaytests/handle_config_change/test-apps/OverlayResApp/src/com/android/overlaytest/overlayresapp/OverlayResTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.overlaytest.overlayresapp;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertNotNull;
+
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.content.om.FabricatedOverlay;
+import android.content.om.OverlayInfo;
+import android.content.om.OverlayManager;
+import android.content.om.OverlayManagerTransaction;
+import android.content.res.Resources;
+import android.os.UserHandle;
+import android.util.TypedValue;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(JUnit4.class)
+public class OverlayResTest {
+    // Default timeout value
+    private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
+    private static final String TEST_OVERLAY_NAME = "Test";
+    private static final String TEST_RESOURCE_INTEGER = "integer/test_integer";
+    private static final String TEST_RESOURCE_STRING = "string/test_string";
+    private static final int TEST_INTEGER = 0;
+    private static final int TEST_FRRO_INTEGER = 1;
+    private static final String TEST_STRING = "Test String";
+    private static final String TEST_FRRO_STRING = "FRRO Test String";
+    private OverlayResActivity mActivity;
+    private Context mContext;
+    private OverlayManager mOverlayManager;
+    private int mUserId;
+    private UserHandle mUserHandle;
+
+    @Rule
+    public ActivityScenarioRule<OverlayResActivity> mActivityScenarioRule =
+            new ActivityScenarioRule<>(OverlayResActivity.class);
+
+    @Before
+    public void setUp() {
+        mActivityScenarioRule.getScenario().onActivity(activity -> {
+            assertThat(activity).isNotNull();
+            mActivity = activity;
+        });
+        mContext = mActivity.getApplicationContext();
+        mOverlayManager = mContext.getSystemService(OverlayManager.class);
+        mUserId = UserHandle.myUserId();
+        mUserHandle = UserHandle.of(mUserId);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        final OverlayManagerTransaction.Builder cleanUp = new OverlayManagerTransaction.Builder();
+        mOverlayManager.getOverlayInfosForTarget(mContext.getPackageName(), mUserHandle).forEach(
+                info -> {
+                    if (info.isFabricated()) {
+                        cleanUp.unregisterFabricatedOverlay(info.getOverlayIdentifier());
+                    }
+                });
+        mOverlayManager.commit(cleanUp.build());
+
+    }
+
+    @Test
+    public void overlayRes_onConfigurationChanged() throws Exception {
+        final CountDownLatch latch1 = new CountDownLatch(1);
+        mActivity.setConfigurationChangedCallback(() -> {
+            Resources r = mActivity.getApplicationContext().getResources();
+            assertThat(r.getInteger(R.integer.test_integer)).isEqualTo(TEST_FRRO_INTEGER);
+            assertThat(r.getString(R.string.test_string)).isEqualTo(TEST_FRRO_STRING);
+            latch1.countDown();
+        });
+
+        // Create and enable FRRO
+        final FabricatedOverlay overlay = new FabricatedOverlay.Builder(
+                mContext.getPackageName(), TEST_OVERLAY_NAME, mContext.getPackageName())
+                .setResourceValue(TEST_RESOURCE_INTEGER, TypedValue.TYPE_INT_DEC, TEST_FRRO_INTEGER)
+                .setResourceValue(TEST_RESOURCE_STRING, TypedValue.TYPE_STRING, TEST_FRRO_STRING)
+                .build();
+
+        mOverlayManager.commit(new OverlayManagerTransaction.Builder()
+                .registerFabricatedOverlay(overlay)
+                .build());
+
+        OverlayInfo info = mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle);
+        assertNotNull(info);
+        assertThat(info.isEnabled()).isFalse();
+
+        mOverlayManager.commit(new OverlayManagerTransaction.Builder()
+                .setEnabled(overlay.getIdentifier(), true, mUserId)
+                .build());
+
+        info = mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle);
+        assertNotNull(info);
+        assertThat(info.isEnabled()).isTrue();
+
+        if (!latch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Fail to wait configuration changes for build and enabling frro, "
+                    + "onConfigurationChanged() has not been invoked.");
+        }
+
+        final CountDownLatch latch2 = new CountDownLatch(1);
+        mActivity.setConfigurationChangedCallback(() -> {
+            Resources r = mActivity.getApplicationContext().getResources();
+            assertThat(r.getInteger(R.integer.test_integer)).isEqualTo(TEST_INTEGER);
+            assertThat(r.getString(R.string.test_string)).isEqualTo(TEST_STRING);
+            latch2.countDown();
+        });
+
+        // unregister FRRO
+        mOverlayManager.commit(new OverlayManagerTransaction.Builder()
+                .unregisterFabricatedOverlay(overlay.getIdentifier())
+                .build());
+
+        if (!latch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Fail to wait configuration changes after unregister frro,"
+                    + " onConfigurationChanged() has not been invoked.");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c8340a9..a1e71a8 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -64,6 +64,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY;
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
+import static android.content.pm.ActivityInfo.CONFIG_RESOURCES_UNUSED;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
 import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
@@ -9839,6 +9840,15 @@
      */
     private boolean shouldRelaunchLocked(int changes, Configuration changesConfig) {
         int configChanged = info.getRealConfigChanged();
+        if (android.content.res.Flags.handleAllConfigChanges()) {
+            if ((configChanged & CONFIG_RESOURCES_UNUSED) != 0) {
+                // Don't relaunch any activities that claim they do not use resources at all.
+                // If they still do, the onConfigurationChanged() callback will get called to
+                // let them know anyway.
+                return false;
+            }
+        }
+
         boolean onlyVrUiModeChanged = onlyVrUiModeChanged(changes, changesConfig);
 
         // Override for apps targeting pre-O sdks
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 669cddb..cbf2c2f 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -18,9 +18,9 @@
 
 #include <unordered_set>
 
-#include "android-base/logging.h"
-
 #include "ResourceUtils.h"
+#include "android-base/logging.h"
+#include "process/SymbolTable.h"
 #include "trace/TraceBuffer.h"
 #include "util/Util.h"
 #include "xml/XmlActionExecutor.h"
@@ -172,6 +172,58 @@
   return NameIsJavaClassName(el, attr, diag);
 }
 
+static bool UpdateConfigChangesIfNeeded(xml::Element* el, IAaptContext* context) {
+  xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "configChanges");
+  if (attr == nullptr) {
+    return true;
+  }
+
+  if (attr->value != "allKnown" && attr->value.find("allKnown") != std::string::npos) {
+    context->GetDiagnostics()->Error(
+        android::DiagMessage(el->line_number)
+        << "If you want to declare 'allKnown' in attribute 'android:configChanges' in <" << el->name
+        << ">, " << attr->value << " is not allowed', allKnown has to be used "
+        << "by itself, for example: 'android:configChanges=allKnown', it cannot be combined with "
+        << "the other flags");
+    return false;
+  }
+
+  if (attr->value == "allKnown") {
+    SymbolTable* symbol_table = context->GetExternalSymbols();
+    const SymbolTable::Symbol* symbol =
+        symbol_table->FindByName(ResourceName("android", ResourceType::kAttr, "configChanges"));
+
+    if (symbol == nullptr) {
+      context->GetDiagnostics()->Error(
+          android::DiagMessage(el->line_number)
+          << "Cannot find symbol for android:configChanges with min sdk: "
+          << context->GetMinSdkVersion());
+    }
+
+    std::stringstream new_value;
+
+    const auto& symbols = symbol->attribute->symbols;
+    for (auto it = symbols.begin(); it != symbols.end(); ++it) {
+      // Skip 'resourcesUnused' which is the flag to fully disable activity restart specifically
+      // for games.
+      if (it->symbol.name.value().entry == "resourcesUnused") {
+        continue;
+      }
+      if (it != symbols.begin()) {
+        new_value << "|";
+      }
+      new_value << it->symbol.name.value().entry;
+    }
+    const auto& old_value = attr->value;
+    auto new_value_str = new_value.str();
+    context->GetDiagnostics()->Note(android::DiagMessage(el->line_number)
+                                    << "Updating value of 'android:configChanges' from "
+                                    << old_value << " to " << new_value_str);
+    attr->value = std::move(new_value_str);
+  }
+  return true;
+}
+
 static bool RequiredNameIsJavaPackage(xml::Element* el, android::SourcePathDiagnostics* diag) {
   xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name");
   if (attr == nullptr) {
@@ -382,8 +434,9 @@
   }
 }
 
-bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, android::IDiagnostics* diag) {
+bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, IAaptContext* context) {
   // First verify some options.
+  android::IDiagnostics* diag = context->GetDiagnostics();
   if (options_.rename_manifest_package) {
     if (!util::IsJavaPackageName(options_.rename_manifest_package.value())) {
       diag->Error(android::DiagMessage() << "invalid manifest package override '"
@@ -432,6 +485,8 @@
   // Common component actions.
   xml::XmlNodeAction component_action;
   component_action.Action(RequiredNameIsJavaClassName);
+  component_action.Action(
+      [context](xml::Element* el) -> bool { return UpdateConfigChangesIfNeeded(el, context); });
   component_action["intent-filter"] = intent_filter_action;
   component_action["preferred"] = intent_filter_action;
   component_action["meta-data"] = meta_data_action;
@@ -778,7 +833,7 @@
   }
 
   xml::XmlActionExecutor executor;
-  if (!BuildRules(&executor, context->GetDiagnostics())) {
+  if (!BuildRules(&executor, context)) {
     return false;
   }
 
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index df0ece6..748a828 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -110,7 +110,7 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(ManifestFixer);
 
-  bool BuildRules(xml::XmlActionExecutor* executor, android::IDiagnostics* diag);
+  bool BuildRules(xml::XmlActionExecutor* executor, IAaptContext* context);
 
   ManifestFixerOptions options_;
 };
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 3cfdf78..5008627 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -37,27 +37,30 @@
             .SetCompilationPackage("android")
             .SetPackageId(0x01)
             .SetNameManglerPolicy(NameManglerPolicy{"android"})
-            .AddSymbolSource(
-                test::StaticSymbolSourceBuilder()
-                    .AddSymbol(
-                        "android:attr/package", ResourceId(0x01010000),
-                        test::AttributeBuilder()
-                            .SetTypeMask(android::ResTable_map::TYPE_STRING)
-                            .Build())
-                    .AddSymbol(
-                        "android:attr/minSdkVersion", ResourceId(0x01010001),
-                        test::AttributeBuilder()
-                            .SetTypeMask(android::ResTable_map::TYPE_STRING |
-                                         android::ResTable_map::TYPE_INTEGER)
-                            .Build())
-                    .AddSymbol(
-                        "android:attr/targetSdkVersion", ResourceId(0x01010002),
-                        test::AttributeBuilder()
-                            .SetTypeMask(android::ResTable_map::TYPE_STRING |
-                                         android::ResTable_map::TYPE_INTEGER)
-                            .Build())
-                    .AddSymbol("android:string/str", ResourceId(0x01060000))
-                    .Build())
+            .AddSymbolSource(test::StaticSymbolSourceBuilder()
+                                 .AddSymbol("android:attr/package", ResourceId(0x01010000),
+                                            test::AttributeBuilder()
+                                                .SetTypeMask(android::ResTable_map::TYPE_STRING)
+                                                .Build())
+                                 .AddSymbol("android:attr/minSdkVersion", ResourceId(0x01010001),
+                                            test::AttributeBuilder()
+                                                .SetTypeMask(android::ResTable_map::TYPE_STRING |
+                                                             android::ResTable_map::TYPE_INTEGER)
+                                                .Build())
+                                 .AddSymbol("android:attr/targetSdkVersion", ResourceId(0x01010002),
+                                            test::AttributeBuilder()
+                                                .SetTypeMask(android::ResTable_map::TYPE_STRING |
+                                                             android::ResTable_map::TYPE_INTEGER)
+                                                .Build())
+                                 .AddSymbol("android:string/str", ResourceId(0x01060000))
+                                 .AddSymbol("android:attr/configChanges", ResourceId(0x01010003),
+                                            test::AttributeBuilder()
+                                                .AddItem("testConfigChange1", 1)
+                                                .AddItem("testConfigChange2", 2)
+                                                .AddItem("resourcesUnused", 4)
+                                                .SetTypeMask(android::ResTable_map::TYPE_STRING)
+                                                .Build())
+                                 .Build())
             .Build();
   }
 
@@ -1591,4 +1594,72 @@
     </manifest>)";
   EXPECT_THAT(Verify(input), NotNull());
 }
+
+TEST_F(ManifestFixerTest, AllKnownNotDeclaredProperly) {
+  std::string input = R"(
+    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="android">
+      <application>
+        <activity android:name=".MainActivity"
+                  android:configChanges="allKnown|testConfigChange1">
+        </activity>
+      </application>
+    </manifest>)";
+  auto doc = Verify(input);
+  EXPECT_THAT(doc, IsNull());
+}
+
+TEST_F(ManifestFixerTest, ModifyAttributeValueForAllKnown) {
+  std::string input = R"(
+    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="android">
+      <application>
+        <activity android:name=".MainActivity"
+                  android:configChanges="allKnown">
+        </activity>
+      </application>
+    </manifest>)";
+  auto doc = Verify(input);
+  EXPECT_THAT(doc, NotNull());
+
+  xml::Element* el;
+  xml::Attribute* attr;
+
+  el = doc->root.get();
+  ASSERT_THAT(el, NotNull());
+  el = el->FindChild({}, "application");
+  ASSERT_THAT(el, NotNull());
+  el = el->FindChild({}, "activity");
+  ASSERT_THAT(el, NotNull());
+
+  attr = el->FindAttribute(xml::kSchemaAndroid, "configChanges");
+  ASSERT_THAT(attr->value, "testConfigChange1|testConfigChange2");
+}
+
+TEST_F(ManifestFixerTest, DoNothingForOtherConfigChanges) {
+  std::string input = R"(
+    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="android">
+      <application>
+        <activity android:name=".MainActivity"
+                  android:configChanges="testConfigChange2">
+        </activity>
+      </application>
+    </manifest>)";
+  auto doc = Verify(input);
+  EXPECT_THAT(doc, NotNull());
+
+  xml::Element* el;
+  xml::Attribute* attr;
+
+  el = doc->root.get();
+  ASSERT_THAT(el, NotNull());
+  el = el->FindChild({}, "application");
+  ASSERT_THAT(el, NotNull());
+  el = el->FindChild({}, "activity");
+  ASSERT_THAT(el, NotNull());
+
+  attr = el->FindAttribute(xml::kSchemaAndroid, "configChanges");
+  ASSERT_THAT(attr->value, "testConfigChange2");
+}
 }  // namespace aapt